local lfs = require("lfs")
local lunajson = require("lunajson")
local t2string = require("t2string")
local gsplit = require("string.gsplit")
---@class configs
---@field xlsxDir string @xlsx目录
---@field jsonDir string @json目录
---@field clientDir string @client目录
---@field serverDir string @server目录
---@field impFile string @导出文件脚本
---@field md5File string @md5码文件
---@field newFile string @操作文件


---加载配置
---@return configs
local function jsonConf()
    if not cfg then
        local path = "./configs.json"
        local file = io.open(path, "r")
        local json = file:read("*a")
        cfg = lunajson.decode(json)
        file:close()
    end

    return cfg
end

---md5码映射
---@param key string|nil @sheets名字
---@return table<string,string>
local function sheets2Excel(key)
    if not sheets2File then
        local path = "./md5.json"
        local file = io.open(path, "r")
        local json = file:read("*a")
        local md5 = lunajson.decode(json)
		sheets2File = {}
		for code,v in pairs(md5) do 
			if v.sheets then
				for i,sName in ipairs(v.sheets) do 
					sheets2File[sName:sub(3,#sName)] = v.name
				end
			end
		end
        file:close()
    end

    if key then 
        return sheets2File[key]
    end
    return sheets2File
end

---更新文件
---@param fname string @文件名字
---@return boolean
local function newFiles(fname)
    if not mapFiles then
        local path = jsonConf().newFile
        local file = io.open(path, "r")
        local json = file:read("*a")
        mapFiles = lunajson.decode(json)
        file:close()
    end

    return mapFiles[fname]
end

---json 列表
---@return string[] @文件列表
local function jsonList()
    local fs = {}
    local dr = jsonConf().jsonDir

    for fname, _ in lfs.dir(dr) do
        local attr = lfs.attributes(dr .. fname)
        if attr.mode ~= "directory" then
            if fname:match(".json$") then
                local key = fname:sub(1, -6)
                if newFiles(key) then
                    table.insert(fs, fname)
                end
            end
        end
    end

    return fs
end

---json 解码
---@return table<string,table>
local function jsonTable()
    local dr = jsonConf().jsonDir
    local fs = jsonList()
    local ts = {}

    for i, f in ipairs(fs) do
        print(f)
        local file = io.open(dr .. f, "r")
        local json = file:read("*a")
        file:close()
        ts[f] = lunajson.decode(json)
    end

    return ts
end

---读取json为table
local function readsTable()
    if not j2lData then
        j2lData = jsonTable()
    end

    return j2lData
end

---翻译中文
---@param v string @配置字符串
---@return string
local function tostringTranslate(v)
    return tostring(v)
end

---猜测类型
---@param v string @配置字符串
---@return boolean|number|string
local function toguess(v)
    if "true" == v then
        return true
    elseif "false" == v then
        return false
    elseif "none" == v or "NaN" == v then
        return nil
    else
        return tonumber(v) or v
    end
end

---猜测-带中文处理
---@param v string @配置字符串
---@return boolean|number|string
local function toguessTranslate(v)
    if "true" == v then
        return true
    elseif "false" == v then
        return false
    elseif "none" == v or "NaN" == v then
        return nil
    else
        return tonumber(v) or v
    end
end

---转bool类型
---@param v string @配置字符串
local function toboolean(v)
    return "true" == v
end

---转number类型
---@param v string @配置字符串
local _tonumber = tonumber
local function tonumber(v)
    if v == "none" or "NaN" == v then
        return
    end

    return _tonumber(v)
end

---转number类型
---@param v string @配置字符串
local _tostring = tostring
local function tostring(v)
    if v == "none" or "NaN" == v then
        return
    end

    return _tostring(v)
end

---转map类型 KV 类型
---@param v string @配置字符串
---@return k,v
local function tomapping(v)
    if v ~= "" and v ~= "none" and "NaN" ~= v then
        local gsp = gsplit(v, ";")
        return toguess(gsp[1]), toguess(gsp[2])
    end
end

---转map类型 KV 类型
---@param v string @配置字符串
---@return k,v
local function tomappingTranslate(v)
    if v ~= "" and v ~= "none" and "NaN" ~= v then
        local gsp = gsplit(v, ";")
        return toguessTranslate(gsp[1]), toguessTranslate(gsp[2])
    end
end

---一维数组解析
local function pairsListT(s, v2f)
    local arr = {}

    for _, v in ipairs(s) do
        if nil ~= v2f(v) then
            table.insert(arr, v2f(v))
        end
    end

    return arr
end

---二维数组解析
local function pairsListM(ms, v2f)
    local arr = {}

    for i, ts in ipairs(ms) do
        local s = gsplit(ts, ";")
        local v = pairsListT(s, v2f)
        table.insert(arr, v)
    end

    return arr
end

---哈希类型解析
local function pairsHashKV(s, v2f)
    local hash = {}

    for _, file in ipairs(s) do
        local k, v = v2f(file)
        if k then
            hash[k] = v
        end
    end


    return hash
end

---二维哈希类型解析
local function pairsHashTKV(tkv, v2f)
    if "none" == tkv[1] or "NaN" == v then
        return
    end

    local ret = {}

    for _, g1 in ipairs(tkv) do
        local g2s = gsplit(g1, ";")
        local k1 = v2f(g2s[1])
        local g3s = gsplit(g2s[2], "#")
        local g3v = {}

        for _, vs in ipairs(g3s) do
            local g4s = gsplit(vs, ":")
            local g4v = pairsListT(g4s, v2f)
            table.insert(g3v, g4v)
        end

        ret[k1] = g3v
    end

    return ret
end

---KTV解析
local function pairsListKTV(s, v2f)
    local mrr = {}

    for i, ls in ipairs(s) do
        local s = gsplit(ls, ";")
        local k = tonumber(s[1]) or s[1]
        local hash = pairsListT(gsplit(s[2], ";"), toguess)
        mrr[k] = hash
    end

    return mrr
end

---数据解析
---@param t  table      @配置数据
---@param ns string[]   @字段名称
---@param ts string[]   @数据类型
---@param gs string[]   @字段归属
---@param lf string     @文件名称
---@return table @解析数据
local function parstab(t, ns, ts, gs, lf)
    local src = {}

    for i, tp in ipairs(ts) do
        if not (t[i] == "none" or t[i] == "NaN") then

            if tp == "Int" then
                src[ns[i]] = tonumber(t[i])
            elseif tp == "Int|T" then
                src[ns[i]] = pairsListT(gsplit(t[i], ";"), tonumber)
            elseif tp == "Int|M" then
                src[ns[i]] = pairsListM(gsplit(t[i], "|"), tonumber)
            elseif tp == "String" then
                src[ns[i]] = tostring(t[i])
            elseif tp == "String|T" then
                src[ns[i]] = pairsListT(gsplit(t[i], ";"), tostring)
            elseif tp == "String|M" then
                src[ns[i]] = pairsListM(gsplit(t[i], "|"), tostring)
            elseif tp == "StringName" then
                src[ns[i]] = tostringTranslate(t[i])
            elseif tp == "StringName|T" then
                src[ns[i]] = pairsListT(gsplit(t[i], ";"), tostringTranslate)
            elseif tp == "StringName|M" then
                src[ns[i]] = pairsListM(gsplit(t[i], "|"), tostringTranslate)
            elseif tp == "Boolean" then
                src[ns[i]] = ("true" == t[i])
            elseif tp == "Boolean|T" then
                src[ns[i]] = pairsListT(gsplit(t[i], ";"), toboolean)
            elseif tp == "Boolean|M" then
                src[ns[i]] = pairsListM(gsplit(t[i], "|"), toboolean)
            elseif tp == "KV" then
                src[ns[i]] = pairsHashKV(gsplit(t[i], "|"), tomapping)
            elseif tp == "KVS" then
                src[ns[i]] = pairsHashKV(gsplit(t[i], "|"), tomappingTranslate)
            elseif tp == "TKV" then
                src[ns[i]] = pairsHashTKV(gsplit(t[i], "|"), toguess)
            elseif tp == "TKVS" then
                src[ns[i]] = pairsHashTKV(gsplit(t[i], "|"), toguessTranslate)
            elseif tp == "KTV" then
                src[ns[i]] = pairsListKTV(gsplit(t[i], "|"), toguess)
            elseif tp == "none" or tp == "NaN" then
            else
                print(false, "warning:" .. tp .. " file:" .. lf)
            end
        end

    end

    local ct = {}
    local st = {}

    for i, g in ipairs(gs) do
        if "server" == g then
            st[ns[i]] = src[ns[i]]
        elseif "client" == g then
            ct[ns[i]] = src[ns[i]]
        elseif "common" == g then
            st[ns[i]] = src[ns[i]]
            ct[ns[i]] = src[ns[i]]
        end
    end

    return ct, st
end

local emmtps = {
    ["Int"] = "number",
    ["Int|T"] = "number[]",
    ["Int|M"] = "number[][]",
    ["String"] = "string",
    ["string|T"] = "string[]",
    ["string|M"] = "string[][]",
    ["StringName"] = "string",
    ["StringName|T"] = "string[]",
    ["StringName|M"] = "string[][]",
    ["Boolean"] = "boolean",
    ["boolean|T"] = "boolean[]",
    ["boolean|M"] = "boolean[][]",
    ["KV"] = "table<boolean|number|string,boolean|number|string>",
    ["KVS"] = "table<boolean|number|string,boolean|number|string>",
    ["TKV"] = "table<boolean|number|string,boolean[][]|number[][]|string[][]>",
    ["TKVS"] = "table<boolean|number|string,boolean[][]|number[][]|string[][]>",
    ["KTV"] = "table<boolean|number|string,boolean[]|number[]|string[]>",
}


---数据解析 
---@param t table[] @原数据
---@param lf 	string @sheets名字
---@return table,table @客户端 服务器
local function parsing(t, lf)

    ---字段注释
    local ds = t[1]

    ---字段名字
    ---@type string[]
    local ns = t[2]

    ---字段类型
    ---@type string[]
    local ts = t[3]

    ---字段归属
    ---@type string[]
    local gs = t[4]

    local cts = {}
    local sts = {}

    ---遍历数据
    for i = 5, #t do
        local ct, st = parstab(t[i], ns, ts, gs, lf)

        if ct[ns[1]] then
            cts[ct[ns[1]]] = ct
        end

        if st[ns[1]] then
            sts[st[ns[1]]] = st
        end
    end

    ---emmylua
	local excel = sheets2Excel(lf:sub(3,-5))
    local emm = {
        "---@class ",
        lf:sub(1,-5),
		" @",
		excel:sub(1,-6)
    }

    for i,field in ipairs(ns) do
        local tpdesc = emmtps[ts[i]]
        if tpdesc then
            table.insert(emm,string.format("\n---@field %s %s @%s",field,tpdesc,ds[i]))
        end
    end

    return cts, sts,table.concat(emm)
end

---lua 写成table
local function writeTable()
    local ts = readsTable()
    local cr = jsonConf().clientDir
    local sr = jsonConf().serverDir

    local emmlua = {}
    for jf, t in pairs(ts) do
        local lf = jf:gsub("json", "lua")
        print(lf)
        local ct, st, em = parsing(t, lf)
        if next(ct) then
            local cfile = io.open(cr .. lf, "w")
            cfile:write("return " .. t2string(ct))
            cfile:close()
        end

        if next(st) then
            local sfile = io.open(sr .. lf, "w")
            sfile:write("return " .. t2string(st))
            sfile:close()
        end

        table.insert(emmlua,{
            lf = lf,
            em = em,
        })
    end
	
	table.sort(emmlua,function(a,b)
		return a.lf < b.lf
	end)

    local import = {
        -- "---@class importCfg @配置导入\n",
        -- "local this = {}\n",
    }
    for i,v in ipairs(emmlua) do
        table.insert(import,"\n")
        table.insert(import,v.em)
        -- table.insert(import,"\n\n---")
        -- table.insert(import,v.lf:sub(1,-5))
        table.insert(import,"\n")
        -- table.insert(import,"---@return table<number,")
        -- table.insert(import,v.lf:sub(1,-5))
        -- table.insert(import,">")
        -- table.insert(import,"\n")
        -- table.insert(import,"function this.")
        -- table.insert(import,v.lf:sub(1,-5))
        -- table.insert(import,"()")
        -- table.insert(import,"\n    return require(\"")
        -- table.insert(import,v.lf:sub(1,-5))
        -- table.insert(import,"\")\nend\n")
    end
    -- table.insert(import,"return this\n")

    local emmy = io.open(jsonConf().impFile, "w")
    emmy:write(table.concat(import))
    emmy:close()

end

print("star lua pairs")
writeTable()
print("close lua pairs")
